package com.maschinenstuermer.profiler;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.List;
import java.util.Properties;

import javax.management.MBeanServer;

import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import com.maschinenstuermer.profiler.config.ConfigFactory;
import com.maschinenstuermer.profiler.model.TraceModelBuilder;
import com.maschinenstuermer.profiler.model.impl.MethodInvocation;
import com.maschinenstuermer.profiler.model.impl.ThreadCallStack;
import com.maschinenstuermer.profiler.model.impl.Trace;
import com.maschinenstuermer.profiler.test.HsqlApp;
import com.maschinenstuermer.profiler.util.TestUtil;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;

@Test(groups="integration")
public class ProfilerAgentIT {
	private static final String TESTME = "testme";
	
	private MBeanServer platformMBeanServer;

	@BeforeMethod
	public void setup() {
		platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
	}
	
	@Test
	public void attach() throws Exception {
		final String agentFilename = getAgentFile();
		String configFile = TestUtil.getResource(ConfigFactory.class, "test-app.config");
		assertNotNull(agentFilename);
		File agentFile = new File(agentFilename);
		assertTrue(agentFile.exists());
		
		System.setProperty(TESTME, "true");

		int numAattchedVms = 0;
		for (final VirtualMachineDescriptor vmDesc : VirtualMachine.list()) {
			VirtualMachine vm = VirtualMachine.attach(vmDesc);
			if (vm.getSystemProperties().containsKey(TESTME)) {
				vm.loadAgent(agentFilename, configFile);
				numAattchedVms++;
			}
			vm.detach();
		}
		
		assertEquals(numAattchedVms, 1,
				"Expected that profiler was attached to exactly one vm!");
	}
	
	@Test(dependsOnMethods="attach")
	public void profile() throws Exception {
		platformMBeanServer.invoke(ProfilerAgent.getProfilerControlName(), "startProfiling", null, null);
		
		HsqlApp hsqlApp = new HsqlApp();
		hsqlApp.run();
		
		final int processedClasses = 
			(Integer) platformMBeanServer.getAttribute(ProfilerAgent.getProfilerControlName(), "ProcessedClasses");
		assertEquals(processedClasses, 0);
		
		platformMBeanServer.invoke(ProfilerAgent.getProfilerControlName(), "stopProfiling", null, null);
		final String fileName = "profiler/" + getClass().getSimpleName() + ".trace";
		platformMBeanServer.invoke(ProfilerAgent.getProfilerControlName(), "save", 
				new Object[] {fileName}, new String[] {String.class.getName()});		
		
		final Trace trace = TraceModelBuilder.load(fileName);
		final List<ThreadCallStack> threadCallStacks = trace.getThreadCallStacks();
		assertEquals(threadCallStacks.size(), 1);
		final ThreadCallStack currentThreadCallStack = threadCallStacks.get(0);
		List<MethodInvocation> methodInvocations = currentThreadCallStack.getMethodInvocations();
		assertEquals(methodInvocations.size(), 1);
		MethodInvocation methodInvocation = methodInvocations.get(0);
		assertEquals(methodInvocation.getMethodName(), 
				HsqlApp.class.getName().concat(".run():void"));
	}

	private String getAgentFile() throws IOException, FileNotFoundException {
		final String profilerArtifactPropertiesFile = TestUtil.getResource(getClass(), "profiler-artifact.properties");
		final Properties artifactProperties = new Properties();
		artifactProperties.load(new FileInputStream(profilerArtifactPropertiesFile));
		final String agentFile = artifactProperties.getProperty("agent.file");
		return agentFile;
	}
}
